/* Copyright 2012 Dustin Mitchell <dmmitche [at] gmail [dot] com>
 *
 * Licensed under the GPLv3.  See included LICENSE.txt for full details.
 */

#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/timerfd.h>

#include "wake_up_sensor.h"
#include "log.h"

static int wake_up_sensor_read (union sensor *s, union sensor_reading *r)
{
  uint64_t n_expirations;

  if (!s || !r || s->sensor.type != SENSOR_WAKE_UP) {
    log (LOG_ERR, "invalid param");
    return -1;
  }

  r->type = SENSOR_WAKE_UP;

  if (read (s->wake_up.clock_fd, &n_expirations, sizeof (n_expirations))
      != sizeof (n_expirations)) {
    log (LOG_ERR, "unable to read timerfd");
    return -1;
  }

  s->wake_up.n_expirations += n_expirations;
  r->wake_up.n_expirations = s->wake_up.n_expirations;

  return 1;
}

static int wake_up_sensor_get_selectable (union sensor *s)
{
  if (!s || s->sensor.type != SENSOR_WAKE_UP) {
    log (LOG_ERR, "invalid param");
    return -1;
  }

  return s->wake_up.clock_fd;
}

static struct sensor_ops wake_up_sensor_ops = {
  .read = wake_up_sensor_read,
  .get_selectable = wake_up_sensor_get_selectable
};



int wake_up_sensor_create (struct wake_up_sensor *wu, int period_ms)
{
  struct itimerspec t_spec;

  if (!wu || period_ms <= 0) {
    log (LOG_ERR, "invalid param");
    return -1;
  }

  if (!sensor_base_create (&wu->base, SENSOR_WAKE_UP,
                           &wake_up_sensor_ops) == -1) {
    log (LOG_ERR, "unable to create sensor_base");
    return -1;
  }

  wu->clock_fd = timerfd_create (CLOCK_MONOTONIC, 0);
  if (wu->clock_fd == -1) {
    log (LOG_ERR, "unable to create timerfd");
    return -1;
  }

  wu->n_expirations = 0;

  t_spec.it_interval.tv_sec = period_ms / 1000;
  t_spec.it_interval.tv_nsec = (period_ms % 1000) * 1000000;
  t_spec.it_value.tv_sec = 0;
  t_spec.it_value.tv_nsec = 1;

  if (timerfd_settime (wu->clock_fd, 0, &t_spec, NULL) == -1) {
    log (LOG_ERR, "unable to set timerfd interval");
    close (wu->clock_fd);
    return -1;
  }

  return 0;
}

void wake_up_sensor_destroy (struct wake_up_sensor *wu)
{
  if (!wu) {
    log (LOG_ERR, "invalid param");
    return;
  }

  close (wu->clock_fd);
}
